import * as THREE from 'three';

// 加载器  为场景添加模型
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
/**
 * 一个用于加载经过Draco压缩的图形库。
 * Draco是一个开源的库，主要用于压缩和解压缩三维模型及点云。 以客户端上解压缩为代价，显著减少压缩的图形。
 * 独立的Draco文件后缀为.drc，其中包含顶点坐标，法线，颜色和其他的属性， Draco文件*不*包含材质，纹理，动画和节点结构-为了能使用这些特征，需要将Draco图形 嵌入到GLTF文件中。
 * 使用glTF-Pipeline可以将一个普通的GLTF文件转化为经过Draco压缩的GLTF文件。 
 * 当使用Draco压缩的GLTF模型时，GLTFLoader内部会调用DRACOLoader。
 * 推荐创建一个DRACOLoader实例并重用，可以有效避免重复创建加载多个解压器实例。
 * DRACOLoader是一个插件，必须显示引用
 */
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

/**
 * 为了真正能够让你的场景借助 three.js 来进行显示，
 * 需要以下几个对象：场景（scene）、相机（camera）和渲染器（renderer），
 * 就能透过摄像机渲染出场景。
 * */
const scene = new THREE.Scene();
// scene.background = new THREE.Color("#ccc"); //创建颜色对象
// scene.background = new THREE.Color(0xff00ff); //创建颜色对象
// scene.background = new THREE.Color("rgb(25,56,89)"); //创建颜色对象
// scene.background = new THREE.Color("hsl(45, 90%, 41%)"); //创建颜色对象

// 纹理贴图
const texture = new THREE.TextureLoader().load("/image/pandas.jpg")
scene.background = texture
/**
 * PerspectiveCamera（透视摄像机）
 * 第一个参数是视野角度（FOV）
 *    视野角度就是无论在什么时候，你所能在显示器上看到的场景的范围，它的单位是角度(与弧度区分开)。
 * 第二个参数是长宽比（aspect ratio）
 *    用一个物体的宽除以它的高的值。比如说，当你在一个宽屏电视上播放老电影时，可以看到图像仿佛是被压扁的。
 * 接下来的两个参数是近截面（near）和远截面（far）
 *    当物体某些部分比摄像机的远截面远或者比近截面近的时候，该这些部分将不会被渲染到场景中。
 *    或许现在你不用担心这个值的影响，但未来为了获得更好的渲染性能，你将可以在你的应用程序里去设置它。
 */
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set(12, 10, 15);
camera.lookAt(0, 0, 0);
/**
 * 接下来是渲染器。
 *    在这里用到的 WebGLRenderer 渲染器之外，Three.js 同时提供了其他几种渲染器，当用户所使用的浏览器过于老旧，或者由于其他原因不支持 WebGL 时，可以使用这几种渲染器进行降级。
 */
const renderer = new THREE.WebGLRenderer({ antialias: true }); //抗锯齿
/**
 *    还需要在我们的应用程序里设置一个渲染器的尺寸。比如说，我们可以使用所需要的渲染区域的宽高，来让渲染器渲染出的场景填充满我们的应用程序。
 *    因此，我们可以将渲染器宽高设置为浏览器窗口宽高。
 *    对于性能比较敏感的应用程序来说，可以使用 setSize 传入一个较小的值，例如 window.innerWidth/2 和 window.innerHeight/2，这将使得应用程序在渲染时，以一半的长宽尺寸渲染场景。
 *    如果希望保持应用程序的尺寸，但是以较低的分辨率来渲染，可在调用 setSize 时，将 updateStyle（第三个参数）设为 false。
 *    例如，假设你的 <canvas> 标签现在已经具有了 100% 的宽和高，调用 setSize(window.innerWidth/2, window.innerHeight/2, false) 将使得你的应用程序以四分之一的大小来进行渲染。
 *    最后一步很重要，我们将 renderer（渲染器）的dom元素（renderer.domElement）添加到我们的 HTML 文档中。这就是渲染器用来显示场景给我们看的 <canvas> 元素。
 */
renderer.setSize( window.innerWidth, window.innerHeight );
/**
 *    最后一步很重要，我们将 renderer（渲染器）的dom元素（renderer.domElement）添加到我们的 HTML 文档中。这就是渲染器用来显示场景给我们看的 <canvas> 元素。
 */
document.body.appendChild( renderer.domElement );


// 载入3D模型
/**
 * GLTF加载器（GLTFLoader）------  用于载入glTF 2.0资源的加载器。
 * glTF（gl传输格式）是一种开放格式的规范 （open format specification）， 用于更高效地传输、加载3D内容。
 *    该类文件以JSON（.gltf）格式或二进制（.glb）格式提供， 外部文件存储贴图（.jpg、.png）和额外的二进制数据（.bin）。
 *    一个glTF组件可传输一个或多个场景， 包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像机。
 * GLTFLoader 是一个附加组件，必须显式导入
 */
// Instantiate a loader
const gltfLoader = new GLTFLoader();
// Optional: Provide a DRACOLoader instance to decode compressed mesh data
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( './2.0/Duck/glTF-Draco' );
gltfLoader.setDRACOLoader( dracoLoader );

// Load a glTF resource
gltfLoader.load(
	// resource URL
	'./2.0/Duck/glTF/Duck.gltf',
	// called when the resource is loaded
	function ( gltf ) {
    let model = gltf.scene
    model.traverse(obj => {
      obj.material = new THREE.MeshBasicMaterial({
        color: 0xF9C120,
        transparent: true,
        opacity: 0.6,
      })
    })
    
		scene.add( model );
    
    renderer.render(scene, camera);

		if (resizeDevicePixel(renderer)) {
			const canvas = renderer.domElement
			camera.aspect = canvas.clientWidth / canvas.clientHeight
			camera.updateProjectionMatrix()
		}

		gltf.animations; // Array<THREE.AnimationClip>
		gltf.scene; // THREE.Group
		gltf.scenes; // Array<THREE.Group>
		gltf.cameras; // Array<THREE.Camera>
		gltf.asset; // Object
	},
	// called while loading is progressing
	function ( xhr ) {
    console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
	},
	// called when loading has errors
	function ( error ) {
    console.log( 'An error happened' );
	}
);

//辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(1000);
scene.add(axesHelper);

function resizeDevicePixel(renderer) {
	const canvas = renderer.domElement
	let width = window.innerWidth
	let height = window.innerHeight
	let devicePixelWidth = canvas.width / window.devicePixelRatio
	let devicePixelHeight = canvas.height / window.devicePixelRatio

	const needResize = devicePixelWidth !== width || devicePixelHeight !== height
	if (needResize) {
		renderer.setSize(width, height, false)
	}
	return needResize
}

// remember these initial values
// 窗口调整大小时保持场景比例不变    给定距离  求可见高度
var tanFOV = Math.tan( ( ( Math.PI / 180 ) * camera.fov / 2 ) );
var windowHeight = window.innerHeight;

window.addEventListener( 'resize', onWindowResize, false );

function onWindowResize( event ) {

	camera.aspect = window.innerWidth / window.innerHeight;
	// adjust the FOV  摄像机的视野角度
	camera.fov = ( 360 / Math.PI ) * Math.atan( tanFOV * ( window.innerHeight / windowHeight ) );
	
	camera.updateProjectionMatrix();
	camera.lookAt( scene.position );

	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.render( scene, camera );
    
}